package dbutils

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"

	"gitee.com/haodreams/libs/config"
	"gorm.io/gorm"
)

// func init() {
// 	config.Register("table", &Conf{})
// }

// 需要读取配置的数据库
var confdb *gorm.DB

// SysAppConfFlag 系统标志
const SysAppConfFlag = "app_system_config"

// SetDatabase 设置配置数据库
func SetDatabase(db *gorm.DB, confs ...*AppConf) {
	confdb = db
	conf := new(AppConf)
	// if !db.HasTable(conf) {
	// 	db.CreateTable(conf)
	// }
	db.AutoMigrate(conf)

	count := int64(0)
	err := db.Model(conf).Where("parent=?", SysAppConfFlag).Count(&count).Error
	if err != nil {
		return
	}
	if count == 0 {
		//初始化默认配置
		conf.Parent = SysAppConfFlag
		conf.Desc = "程序日志级别"
		conf.Name = "程序日志级别"
		conf.Key = "app_log_level"
		conf.Value = "info"
		conf.Type = "select"
		conf.Data = "debug|info|warn|error"
		db.Create(conf)
		for _, f := range confs {
			if f.Parent == "" {
				f.Parent = SysAppConfFlag
			}
			if f.Key != "" {
				db.Create(f)
			}
		}
	}
}

// Conf 读取配置文件
type Conf struct {
	Lock *sync.RWMutex
	m    map[string]string
	name string
}

// Conf 读取配置文件

// SaveConfigFile Save config
func (conf *Conf) SaveConfigFile(filename string) error {
	if confdb == nil {
		return errors.New("no database for config")
	}
	conf.Lock.Lock()
	defer conf.Lock.Unlock()

	confdb.Begin()
	for key, value := range conf.m {
		record := &AppConf{Value: value}
		if conf.name != "" {
			newdb := confdb.Model(record).Where("key = ? and parent = ?", key, conf.name).Updates(record)
			if newdb.Error == nil && newdb.RowsAffected == 0 {
				confdb.Create(record)
			}
		} else {
			newdb := confdb.Model(record).Where("key = ? ", key).Updates(record)
			if newdb.Error == nil && newdb.RowsAffected == 0 {
				confdb.Create(record)
			}
		}
	}
	confdb.Commit()
	return nil
}

// Parse 从关系库表中读取配置
func (conf *Conf) Parse(key string) (c config.Configer, err error) {
	if conf.Lock == nil {
		conf.Lock = new(sync.RWMutex)
	}
	if conf.m == nil {
		conf.m = map[string]string{}
	}
	conf.name = key
	conf.Lock.Lock()
	defer conf.Lock.Unlock()
	var rows []*AppConf

	rows, err = Find(conf.name)
	if err != nil {
		return
	}
	for _, row := range rows {
		key := row.Key
		value := row.Value
		key = strings.TrimSpace(key)
		value = strings.TrimSpace(value)
		conf.m[key] = value
	}
	c = conf
	return
}

func NewConf() *Conf {
	c := new(Conf)
	c.Lock = new(sync.RWMutex)
	c.m = map[string]string{}
	return c
}

func NewFromMap(m map[string]string) *Conf {
	c := new(Conf)
	c.m = m
	return c
}

// Set 重设指定的Key Value
func (conf *Conf) Set(key, val string) (err error) { //support section::key type in given key when using ini type.
	if len(key) == 0 {
		return errors.New("key is empty")
	}
	conf.Lock.Lock()
	defer conf.Lock.Unlock()
	conf.m[key] = val
	return
}

// String Get string value
func (conf *Conf) String(key string) (s string) { //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
	conf.Lock.RLock()
	defer conf.Lock.RUnlock()
	s = conf.m[key]
	return
}

// Strings 没实现
func (conf *Conf) Strings(key string) (ss []string) { //get string slice
	return
}

// Int Get int value by key
func (conf *Conf) Int(key string) (int, error) {
	conf.Lock.RLock()
	defer conf.Lock.RUnlock()
	s := conf.m[key]
	return strconv.Atoi(s)
}

// Int64 Get int64 value by key
func (conf *Conf) Int64(key string) (int64, error) {
	conf.Lock.RLock()
	defer conf.Lock.RUnlock()
	s := conf.m[key]
	return strconv.ParseInt(s, 10, 64)
}

// Bool Get bool value by key
func (conf *Conf) Bool(key string) (bool, error) {
	conf.Lock.RLock()
	defer conf.Lock.RUnlock()
	s := conf.m[key]
	return ParseBool(s)
}

// Float Get float64 by key
func (conf *Conf) Float(key string) (float64, error) {
	conf.Lock.RLock()
	defer conf.Lock.RUnlock()
	s := conf.m[key]
	return strconv.ParseFloat(s, 64)
}

// DefaultString Get value by key, if value is not exist, return defaultValue
func (conf *Conf) DefaultString(key string, defaultVal string) string { // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
	conf.Lock.RLock()
	defer conf.Lock.RUnlock()
	s, ok := conf.m[key]
	if ok {
		return s
	}
	return defaultVal
}

// DefaultStrings Not implementation
func (conf *Conf) DefaultStrings(key string, defaultVal []string) []string {
	return nil
}

// DefaultInt Get int value by key, if value is not exist, return defaultValue
func (conf *Conf) DefaultInt(key string, defaultVal int) int {
	i, err := conf.Int(key)
	if err != nil {
		return defaultVal
	}
	return i
}

// DefaultInt64 int64 value by key, if value is not exist, return defaultValue
func (conf *Conf) DefaultInt64(key string, defaultVal int64) int64 {
	i, err := conf.Int64(key)
	if err != nil {
		return defaultVal
	}
	return i
}

// DefaultBool int64 value by key, if value is not exist, return defaultValue
func (conf *Conf) DefaultBool(key string, defaultVal bool) bool {
	b, err := conf.Bool(key)
	if err != nil {
		return defaultVal
	}
	return b
}

// DefaultFloat float64 value by key, if value is not exist, return defaultValue
func (conf *Conf) DefaultFloat(key string, defaultVal float64) float64 {
	f, err := conf.Float(key)
	if err != nil {
		return defaultVal
	}
	return f
}

// DIY Not implementation
// TODO 暂没实现
func (conf *Conf) DIY(key string) (interface{}, error) {
	return nil, nil
}

// GetSection Not implementation
func (conf *Conf) GetSection(section string) (map[string]string, error) {
	return nil, nil
}

// ParseData Not implementation
func (conf *Conf) ParseData(data []byte) (config.Configer, error) {
	return conf, errors.New("not support")
}

// ParseBool 格式化布尔类型
func ParseBool(val interface{}) (value bool, err error) {
	if val != nil {
		switch v := val.(type) {
		case bool:
			return v, nil
		case string:
			switch v {
			case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On":
				return true, nil
			case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off":
				return false, nil
			}
		case int8, int32, int64:
			strV := fmt.Sprintf("%s", v)
			if strV == "1" {
				return true, nil
			} else if strV == "0" {
				return false, nil
			}
		case float64:
			if v == 1 {
				return true, nil
			} else if v == 0 {
				return false, nil
			}
		}
		return false, fmt.Errorf("parsing %q: invalid syntax", val)
	}
	return false, fmt.Errorf("parsing <nil>: invalid syntax")
}
